home *** CD-ROM | disk | FTP | other *** search
/ Mac Magazin/MacEasy 19 / Mac Magazin and MacEasy Magazine CD - Issue 19.iso / Musik & Kunst / Ear Workout 2.1 / source code / ear_main.cp < prev    next >
Text File  |  1996-01-15  |  17KB  |  690 lines

  1. //
  2. // Ear Workout, by Ben Crowell
  3. //
  4. // Version history:
  5. //   version 1.0  -  21 Nov 95
  6. //        Includes two workouts: chords and intervals.
  7. //   version 2.0  -  9 Jan 96
  8. //        New workout: singing intervals (currently works ok
  9. //            with whistling, but not as well with singing).
  10. //
  11. // Ideas for new workouts:
  12. //    Play scale, play chord, they identify chord w.r.t. key.
  13. //    Rhythm: reading? rhythmic canons? cross-rhythms?
  14. //    Tetrachords; identify by mac-style click and select
  15. //        across the scale
  16. //      Identify inversions of chords.
  17. //
  18. // Improvements to make:
  19. //   Staff notation could look a lot better.
  20. //   Misc possible improvements in chords: see ear_chords.cp
  21. //   Should store waveforms in a resource, instead of computing them
  22. //   when we start up.
  23. //   Implementation of volume control is not so great: should
  24. //   highlight the previous volume selection.
  25. //   Timbre of synthesized notes is not exactly beautiful.
  26. //   Subroutine bail_out() should give a dialog box, not just write to a file.
  27. //
  28. // Bugs:
  29. //   If it's already running (no workout open), and you double-click
  30. //   on it in the finder, you get a watch cursor that never changes
  31. //   to an arrow.  Also may crash.
  32. //
  33. //
  34.  
  35. #include <string.h>
  36. #include <stdlib.h>
  37. #include <stdio.h>
  38. #include <math.h>
  39.  
  40. #include <OSUtils.h>
  41. #include <QuickDraw.h>
  42. #include <Sound.h>
  43.  
  44. #define NEED_MAC_STUFF 1
  45.  
  46. #include "Ninkasi:C++ util:generic.h"
  47. #include "Ninkasi:C++ util:complete_window.h"
  48. #include "ear_defines.h"
  49. #include "ear_globals.h"
  50. #include "ear_prototypes.h"
  51.  
  52. void init_mac_stuff();
  53. void wrap_it_up();
  54. void set_up_menus(int resource_id,int apple_menu_id);
  55. void event_loop();
  56. void do_mouse_down(EventRecord *my_event);
  57. void do_controls(WindowPtr window,Point where,EventRecord *my_event);
  58. void do_menu(long command);
  59.  
  60. typedef pascal void (*track_control_filter_ptr_t)(ControlRecord**,short);
  61.  
  62.  
  63. Rect drag_rect;
  64.  
  65. void make_intervals_window();
  66. void make_chord_window();
  67. void make_about_ear_training_window();
  68. void make_sing_interval_window();
  69. DEN_MOTHER_T interval_den_mother,chord_den_mother,about_ear_training_den_mother,
  70.     chord_help_den_mother,about_chord_den_mother,sing_interval_den_mother;
  71. int which_complete_window(WindowPtr w);
  72. void do_update(EventRecord *my_event);
  73. void do_activate(EventRecord *my_event);
  74. void do_key(EventRecord *my_event);
  75. void select_initial_workout();
  76.  
  77.  
  78. //-------------------------------------------------------------
  79.     void
  80. main()
  81.         {
  82.         
  83.       
  84.          
  85.           init_mac_stuff();
  86.                     
  87.           set_up_menus(MBAR_ID,APPLE_MENU_ID);   
  88.           watch_cursor();
  89.           set_up_sound();
  90.       arrow_cursor();
  91.       normal_text_style();
  92.       
  93.  
  94.           select_initial_workout();
  95.           
  96.           event_loop();
  97.           
  98.           //--should never drop through here, but just in case:
  99.           
  100.           wrap_it_up();             
  101.     }
  102.  
  103.     //... called at beginning of progra; can also be called later
  104.     //    to reinstall everything (without recalculating wave table
  105.     void
  106. set_up_sound()
  107.     {
  108.       do_waveforms(my_snd_chan,4,0.15,chans_used,my_wave_table,
  109.             temp_wave_table,WAVE_TABLE_SIZE);
  110.     }
  111.  
  112.     void
  113. select_initial_workout()
  114.     {
  115.       DialogPtr my_dialog;
  116.       short item_hit;
  117.       my_dialog = GetNewDialog(SELECT_INITIAL_WORKOUT_MODAL_DLOG_ID,
  118.                 0,(WindowPtr) -1);
  119.       if (VALID_POINTER(my_dialog)) {
  120.         ModalDialog(0,&item_hit);
  121.         DisposDialog(my_dialog);
  122.         switch(item_hit) {
  123.           case 1: //-- chords
  124.             make_chord_window();
  125.             break;
  126.           case 2: //-- intervals
  127.             make_intervals_window();
  128.             break;
  129.           case 4: //-- no workout
  130.             break;
  131.           case 5: //-- quit
  132.             wrap_it_up();
  133.             break;
  134.           case 6: //-- sing intervals
  135.             make_sing_interval_window();
  136.             break;
  137.         }
  138.       }
  139.     }
  140.  
  141.     void
  142. set_volume()
  143.     {
  144.       DialogPtr my_dialog;
  145.       short item_hit;
  146.       short vol,z;
  147.       int i;
  148.       int get_current_volume(SndChannelPtr snd_chan);
  149.       void set_volume(int new_volume,SndChannelPtr snd_chan);
  150.       my_dialog = GetNewDialog(VOLUME_MODAL_DLOG_ID,
  151.                 0,(WindowPtr) -1);
  152.           /*
  153.       vol = 7 & ((GetSysPPtr()->volClik)>>8);
  154.       */
  155.       vol = 0;
  156.       vol = get_current_volume(my_snd_chan[0])/(512/8)-1;
  157.           if (vol>=1 && vol<=7) SetDialogDefaultItem(my_dialog,(short) vol);
  158.       if (VALID_POINTER(my_dialog)) {
  159.         ModalDialog(0,&item_hit);
  160.         DisposDialog(my_dialog);
  161.         switch(item_hit) {
  162.           case 1: 
  163.           case 2: 
  164.           case 3: 
  165.           case 4: 
  166.           case 5: 
  167.           case 6: 
  168.           case 7:
  169.             vol = item_hit;
  170.  
  171. #if 0
  172.             z = GetSysPPtr()->volClik;
  173.             z = (z & 0xf8ff) | (vol<<8);
  174.             dispose_of_chans(chans_used,my_snd_chan);
  175.             GetSysPPtr()->volClik = z;
  176.             WriteParam(); //...cf InitUtil
  177.             InitUtil();
  178.             set_up_sound();
  179. #endif
  180.             
  181. #if 1
  182.             // The above stuff with the system parameter block didn't
  183.             //  work.  Apparently there is some magic way of notifying
  184.             //  the os that you've changed the volume, but I can't figure
  185.             //  out what it is.  Instead, force it to recalculate the
  186.             //  waveform, which is time-consuming:
  187.             
  188.             have_wave_table = 0;
  189.             do_waveforms(my_snd_chan,4,0.15*vol/7.,chans_used,my_wave_table,
  190.             temp_wave_table,WAVE_TABLE_SIZE);
  191. #endif
  192.  
  193.         // Doesn't work:
  194. #if 0
  195.             for (i=0; i<chans_used; i++)
  196.               set_volume(vol,my_snd_chan[i]);
  197. #endif
  198.  
  199.             break;
  200.           case 8:
  201.             break;
  202.         }
  203.       }
  204.     }
  205.  
  206.     void
  207. wrap_it_up()
  208.     {
  209.       int i;
  210.       for (i=0; i<n_windows; i++) {
  211.         delete my_windows[i];
  212.       }
  213.       dispose_of_chans(chans_used,my_snd_chan);
  214.           if (have_sample_storage) {
  215.             if (VALID_HANDLE(sound_handle)) DisposHandle(sound_handle);
  216.             have_sample_storage = 0;
  217.           }
  218.       ExitToShell();
  219.     }
  220.  
  221.     void
  222. init_mac_stuff()
  223.     {
  224.           InitGraf(&thePort);
  225.           init_random(); /* must do InitGraf first!!!!!! */
  226.           InitFonts();
  227.           FlushEvents(everyEvent,0);
  228.           InitWindows();
  229.       InitMenus();
  230.           InitCursor();
  231.           //if (OpenResFile("\pmy.rsrc") == -1)
  232.           //  bail_out("could not open resource file");
  233.           resources_available = 1;
  234.           drag_rect = thePort->portRect;
  235.           TextSize((short) normal_font_size);
  236.           TEInit();
  237.     }
  238.  
  239.     void
  240. make_window(int dlog_resource_id,
  241.         Str255 param_text0,
  242.         Str255 param_text1,
  243.         Str255 param_text2,
  244.         Str255 param_text3,
  245.         DEN_MOTHER_T *den_mother)
  246.     {
  247.       if (n_windows>=MAX_WINDOWS) return;
  248.       ++n_windows;
  249.       my_windows[n_windows-1] = new complete_window(dlog_resource_id,
  250.               param_text0,param_text1,param_text2,param_text3,
  251.               den_mother);
  252.               
  253.       if (!VALID_POINTER(my_windows[n_windows-1])
  254.           || !my_windows[n_windows-1]->is_valid) {
  255.         --n_windows;
  256.       }
  257.     }
  258.  
  259.     void
  260. remove_window(int which)
  261.     {
  262.       int i;
  263.       if (which>=n_windows) return;
  264.       if (VALID_POINTER(my_windows[which])) {
  265.         delete my_windows[which];
  266.                 //-- destructor will call the den mother and
  267.                 //    do a DisposDialog
  268.         for (i=which; i<=n_windows-2; i++) {
  269.           my_windows[i] = my_windows[i+1];
  270.         }
  271.       }
  272.       --n_windows;
  273.     }
  274.  
  275.     int
  276. which_complete_window(WindowPtr w)
  277.     {
  278.       int i;
  279.       for (i=0; i<n_windows; i++) {
  280.         if (my_windows[i]->the_window==w) return i;
  281.       }
  282.       return -1;
  283.     }
  284.  
  285.     void
  286. event_loop()
  287.     {
  288.       EventRecord my_event;
  289.       int valid;
  290.       for (;;) {
  291.         SystemTask();
  292.         valid = GetNextEvent(everyEvent,&my_event);
  293.         if (valid) {
  294.           switch(my_event.what) {
  295.             case keyDown:
  296.               do_key(&my_event);
  297.               break;
  298.             case mouseDown:
  299.               do_mouse_down(&my_event);
  300.               break;
  301.             case activateEvt:
  302.               do_activate(&my_event);
  303.               break;
  304.             case updateEvt:
  305.               do_update(&my_event);
  306.               break;
  307.             default:
  308.               break;
  309.           } //-- end switch
  310.         } //-- end if valid
  311.       } //-- end for (;;)
  312.     }
  313.  
  314.     void
  315. do_key(EventRecord *my_event)
  316.     {
  317.             long menu_result;
  318.             int is_menu;
  319.             is_menu = 0;
  320.             if ((my_event->modifiers & cmdKey)!=0) {
  321.               menu_result = MenuKey((char) (my_event->message & charCodeMask));
  322.               is_menu = (HiWord(menu_result)!=0);
  323.             }
  324.             if (is_menu) {
  325.               do_menu(menu_result);
  326.             }
  327.             else {
  328.               //-- handle keystrokes
  329.             }
  330.     }
  331.  
  332.     void
  333. do_update(EventRecord *my_event)
  334.     {
  335.       GrafPtr save_graf;
  336.       WindowPtr window;
  337.       int which;
  338.       complete_window *the_complete_window;
  339.       window = (WindowPtr) my_event->message;
  340.       which = which_complete_window(window);
  341.       if (which>=0) {
  342.         GetPort(&save_graf);
  343.         SetPort(window);
  344.         BeginUpdate(window);
  345.         
  346.         EraseRect(&window->portRect);
  347.         DrawControls(window);
  348.         
  349.         the_complete_window = my_windows[which];
  350.         the_complete_window->whassup = complete_window_redraw;
  351.         the_complete_window->the_event = my_event;
  352.         (*(the_complete_window->den_mother))(the_complete_window);
  353.         
  354.         EndUpdate(window);
  355.         SetPort(save_graf);
  356.       }
  357.     }
  358.     
  359.     
  360.     void
  361. do_activate(EventRecord *my_event)
  362.     {
  363.       if (my_event->modifiers & 1)
  364.         SetPort((WindowPtr) (my_event->message)); //--activate event
  365.       else
  366.         ; //--deactivate event
  367.     }
  368.     
  369.     //------- watch out that memory doesn't move, orphaning mouse_window
  370.     void
  371. do_mouse_down(EventRecord *my_event)
  372.     {
  373.       WindowPtr mouse_window;
  374.       int which_part;
  375.       which_part = FindWindow(my_event->where,&mouse_window);
  376.       switch(which_part) {
  377.         case inMenuBar:
  378.           do_menu(MenuSelect(my_event->where));
  379.           break;
  380.         case inSysWindow:
  381.           SystemClick(my_event,mouse_window);
  382.           break;
  383.         case inGoAway:
  384.           {
  385.           int which;
  386.           complete_window *the_complete_window;
  387.           which = which_complete_window(mouse_window);
  388.           if (which != -1) {
  389.             remove_window(which);
  390.           }
  391.           }      
  392.           break;
  393.         case inContent:
  394.           if (mouse_window != FrontWindow())
  395.             SelectWindow(mouse_window);
  396.           else
  397.             do_controls(mouse_window,my_event->where,my_event);
  398.           break;
  399.         case inDrag:
  400.           DragWindow(mouse_window,my_event->where,&drag_rect);
  401.           break;
  402.         default:
  403.           break;
  404.       }
  405.     }
  406.     
  407.     void
  408. do_menu(long command)
  409.     {
  410.       int menu_id,item;
  411.       menu_id = HiWord(command);
  412.       item = LoWord(command);
  413.       
  414.       switch(menu_id) {
  415.         case APPLE_MENU_ID:
  416.           {
  417.             unsigned char item_name[32];
  418.             switch(item) {
  419.               case 1:
  420.                 //-- about Ear Training
  421.                 make_about_ear_training_window();
  422.                 break;
  423.               default:
  424.                 GetItem(GetMHandle((short) menu_id),(short) item,item_name);
  425.                 OpenDeskAcc(item_name);
  426.                 break;
  427.             }
  428.           }
  429.           break;
  430.         case FILE_MENU_ID:
  431.           ExitToShell();
  432.           break;
  433.         case EDIT_MENU_ID:
  434.           if (item<=6 && item!=2) {
  435.           int which;
  436.           if (!SystemEdit(item-1)) {
  437.                //...returns 0 if we should handle it
  438.             which = which_complete_window(FrontWindow());
  439.             if (which != -1) {
  440.               message_to_den_mother(my_windows[which],
  441.                 (void **) 0,(void *) 0,(int) item,"edit");
  442.             }
  443.           }
  444.           }      
  445.           break;
  446.         case WORKOUTS_MENU_ID:
  447.           switch(item) {
  448.             case 1:
  449.               make_intervals_window();
  450.               break;
  451.             case 2:
  452.               make_chord_window();
  453.               break;
  454.             case 3:
  455.               make_sing_interval_window();
  456.               break;
  457.           }
  458.           break;
  459.         case OPTIONS_MENU_ID:
  460.           switch(item) {
  461.             case 1: //-- volume
  462.               set_volume();
  463.               break;
  464.           }
  465.       }
  466.       HiliteMenu(0);
  467.     }
  468.  
  469.     void
  470. do_controls(WindowPtr window,Point where,EventRecord *my_event)
  471.     {
  472.       int which;
  473.       complete_window *the_complete_window;
  474.       ControlHandle control;
  475.       
  476.       which = which_complete_window(window);
  477.       if (which== -1) bail_out("unknown window in do_controls");
  478.       the_complete_window = my_windows[which];
  479.       if (!VALID_POINTER(the_complete_window)
  480.            || !the_complete_window->is_valid
  481.            || !VALID_POINTER(the_complete_window->den_mother))
  482.         bail_out("window is garbage in do_controls");
  483.       
  484.       GlobalToLocal(&where); 
  485.           //---this assumes that the routine do_activate
  486.           //    has set the graf port to be _this_ window
  487.       the_complete_window->part_code = FindControl(where,window,&control);
  488.       
  489.       if (!TrackControl(control,where,(track_control_filter_ptr_t) 0)) return;
  490.  
  491.       if (!the_complete_window->part_code) return;
  492.       the_complete_window->part_number
  493.           = FindDItem(window,where);
  494.       if (the_complete_window->part_number == -1) return;
  495.  
  496.       the_complete_window->whassup = complete_window_action;
  497.       the_complete_window->the_event = my_event;
  498.       (*(the_complete_window->den_mother))(the_complete_window);
  499.     }
  500.  
  501.     void
  502. make_chord_help_window()
  503.     {
  504.       if (!chord_help_window_exists) {
  505.         make_window(CHORD_HELP_DLOG_ID,"\p","\p","\p","\p",
  506.               &chord_help_den_mother);
  507.         chord_help_window_exists = 1;
  508.       }
  509.     }
  510.  
  511.     void
  512. make_about_ear_training_window()
  513.     {
  514.       if (!about_ear_training_window_exists) {
  515.         make_window(ABOUT_EAR_TRAINING_DLOG_ID,"\p","\p","\p","\p",
  516.               &about_ear_training_den_mother);
  517.         about_ear_training_window_exists = 1;
  518.       }
  519.     }
  520.  
  521.     void
  522. make_intervals_window()
  523.     {
  524.       if (!interval_window_exists) {
  525.         make_window(INTERVAL_DLOG_ID,"\p","\p","\p","\p",
  526.               &interval_den_mother);
  527.         interval_window_exists = 1;
  528.       }
  529.     }
  530.  
  531.     void
  532. make_sing_interval_window()
  533.     {
  534.       if (!sing_interval_window_exists) {
  535.         make_window(SING_INTERVAL_DLOG_ID,"\p","\p","\p","\p",
  536.               &sing_interval_den_mother);
  537.         sing_interval_window_exists = 1;
  538.       }
  539.     }
  540.  
  541.     void
  542. make_chord_window()
  543.     {
  544.       if (!chord_window_exists) {
  545.         make_window(CHORD_DLOG_ID,"\p","\p","\p","\p",
  546.               &chord_den_mother);
  547.         chord_window_exists = 1;
  548.       }
  549.     }
  550.  
  551.  
  552.     // should check if resources_available, and if so, use dialog box
  553.     void
  554. bail_out(char *message)
  555.     {
  556.       SysBeep((short) 0);
  557.       debug_print(message);
  558.       wrap_it_up();
  559.     }
  560.  
  561.  
  562.     void
  563. set_up_menus(int resource_id,int apple_menu_id)
  564.     {
  565.       Handle mbar_handle;
  566.       MenuHandle apple_menu;
  567.       mbar_handle = GetNewMBar((short) resource_id);
  568.       if (ResError()!=0 || !VALID_HANDLE(mbar_handle) )
  569.         bail_out("error reading menu bar");
  570.       SetMenuBar(mbar_handle);
  571.       apple_menu = GetMHandle((short) apple_menu_id);
  572.       if (!VALID_HANDLE(apple_menu) )
  573.         bail_out("error getting apple menu");
  574.       AddResMenu(apple_menu,'DRVR');
  575.       DrawMenuBar();
  576.     }
  577.  
  578.     void
  579. about_ear_training_den_mother(complete_window *my_complete_window)
  580.     {
  581.       int need_to_redraw,ready_to_update;
  582.       GrafPtr save_graf;
  583.       
  584.       need_to_redraw = 0;
  585.       ready_to_update = 0;
  586.       
  587.       switch(my_complete_window->whassup) {
  588.         case complete_window_created:
  589.           need_to_redraw = 1;
  590.           break;
  591.         case complete_window_redraw:
  592.           need_to_redraw = 1;
  593.           ready_to_update = 1; //-- main program does begin update & sets graf port
  594.           break;
  595.         case complete_window_action:
  596.           break;
  597.         case complete_window_erase:
  598.           about_ear_training_window_exists = 0;
  599.           return;
  600.       }
  601.       
  602.       if (need_to_redraw) {
  603.         if (!ready_to_update) {
  604.           GetPort(&save_graf);
  605.           SetPort(my_complete_window->the_window);
  606.         }
  607.         {
  608.           short tt;
  609.           Handle hh;
  610.           Rect rr;
  611.           unsigned char s[300];
  612.           TextFont((short) newYork);
  613.           GetDItem(my_complete_window->the_window,(short) 1,&tt,&hh,&rr);
  614.           if (VALID_HANDLE(hh)) {
  615.             GetIText(hh,s);
  616.             SetIText(hh,s);
  617.           }
  618.           GetDItem(my_complete_window->the_window,(short) 2,&tt,&hh,&rr);
  619.           if (VALID_HANDLE(hh)) {
  620.             GetIText(hh,s);
  621.             SetIText(hh,s);
  622.           }
  623.           TextSize((short) 10);
  624.           GetDItem(my_complete_window->the_window,(short) 3,&tt,&hh,&rr);
  625.           if (VALID_HANDLE(hh)) {
  626.             GetIText(hh,s);
  627.             SetIText(hh,s);
  628.           }
  629.           normal_text_style();
  630.         }
  631.         if (!ready_to_update) {
  632.           SetPort(save_graf);
  633.         }
  634.       }//---end if need to redraw
  635.  
  636.     }
  637.  
  638.     void
  639. chord_help_den_mother(complete_window *my_complete_window)
  640.     {
  641.       int need_to_redraw,ready_to_update;
  642.       GrafPtr save_graf;
  643.       
  644.       need_to_redraw = 0;
  645.       ready_to_update = 0;
  646.       
  647.       switch(my_complete_window->whassup) {
  648.         case complete_window_created:
  649.           need_to_redraw = 1;
  650.           break;
  651.         case complete_window_redraw:
  652.           need_to_redraw = 1;
  653.           ready_to_update = 1; //-- main program does begin update & sets graf port
  654.           break;
  655.         case complete_window_action:
  656.           break;
  657.         case complete_window_erase:
  658.           chord_help_window_exists = 0;
  659.           return;
  660.       }
  661.       
  662.       if (need_to_redraw) {
  663.         if (!ready_to_update) {
  664.           GetPort(&save_graf);
  665.           SetPort(my_complete_window->the_window);
  666.         }
  667.         TextFont((short) geneva);
  668.         {
  669.           int i;
  670.           short tt;
  671.           Handle hh;
  672.           Rect rr;
  673.           unsigned char s[300];
  674.           for (i=1; i<=7; i++) {
  675.             GetDItem(my_complete_window->the_window,(short) i,&tt,&hh,&rr);
  676.             if (VALID_HANDLE(hh)) {
  677.               GetIText(hh,s);
  678.               SetIText(hh,s);
  679.             }
  680.           }
  681.         }
  682.         normal_text_style();
  683.         if (!ready_to_update) {
  684.           SetPort(save_graf);
  685.         }
  686.       }//---end if need to redraw
  687.  
  688.     }
  689.  
  690.